﻿@using System.Globalization
@using Microsoft.FluentUI.AspNetCore.Components.Components.DateTime
@using Microsoft.FluentUI.AspNetCore.Components.Extensions
@using Xunit
@inherits TestContext

@code
{
    private const string DAY_JUNE_5 = "2022-06-05";
    private const string DAY_JUNE_6 = "2022-06-06";
    private const string DAY_JUNE_7 = "2022-06-07";
    private const string DAY_JUNE_22 = "2022-06-22";
    private static readonly RangeOfDates RANGE_5_6_7 = new RangeOfDates(DateTime.Parse(DAY_JUNE_5), DateTime.Parse(DAY_JUNE_7));

    [Fact]
    public void FluentCalendarSelection_SelectModeSingle()
    {
        DateTime? SelectedDay = null;

        // Arrange
        var calendar = Render(
            @<FluentCalendar SelectMode="CalendarSelectMode.Single"
                @bind-Value="@SelectedDay"
                PickerMonth="@DateTime.Parse(DAY_JUNE_5)"
                DisabledDateFunc="@DisabledDay_3_8_20" />);

        // Select first date
        calendar.Find($"div[value='{DAY_JUNE_5}']")?.Click();
        Assert.Equal(DateTime.Parse(DAY_JUNE_5), SelectedDay);

        // Select second date
        calendar.Find($"div[value='{DAY_JUNE_7}']")?.Click();
        Assert.Equal(DateTime.Parse(DAY_JUNE_7), SelectedDay);
    }

    [Fact]
    public void FluentCalendarSelection_SelectModeRange()
    {
        IEnumerable<DateTime> SelectedRange = [];

        // Arrange
        var calendar = Render(
            @<FluentCalendar SelectMode="CalendarSelectMode.Range"
                @bind-SelectedDates="@SelectedRange"
                PickerMonth="@DateTime.Parse(DAY_JUNE_5)"
                DisabledDateFunc="@DisabledDay_3_8_20" />);

        // Act: select first date, and then the second date
        calendar.Find($"div[value='{DAY_JUNE_5}']")?.Click();
        calendar.Find($"div[value='{DAY_JUNE_7}']")?.Click();

        // Assert
        Assert.Equal(RANGE_5_6_7.GetAllDates(), SelectedRange);
    }

    [Fact]
    public void FluentCalendarSelection_SelectModeRange_Clear()
    {
        IEnumerable<DateTime> SelectedRange = [];

        // Arrange
        var calendar = Render(
            @<FluentCalendar SelectMode="CalendarSelectMode.Range"
                @bind-SelectedDates="@SelectedRange"
                PickerMonth="@DateTime.Parse(DAY_JUNE_5)"
                DisabledDateFunc="@DisabledDay_3_8_20" />);

        // Act: select first date, and then the second date
        calendar.Find($"div[value='{DAY_JUNE_5}']")?.Click();
        calendar.Find($"div[value='{DAY_JUNE_7}']")?.Click();

        // Clear the selection, click on a date inside the selection
        calendar.Find($"div[value='{DAY_JUNE_6}']")?.Click();

        // Assert
        Assert.Empty(SelectedRange);
    }  

    [Fact]
    public void FluentCalendarSelection_SelectModeRange_NewSelection()
    {
        IEnumerable<DateTime> SelectedRange = [];

        // Arrange
        var calendar = Render(
            @<FluentCalendar SelectMode="CalendarSelectMode.Range"
                @bind-SelectedDates="@SelectedRange"
                PickerMonth="@DateTime.Parse(DAY_JUNE_5)"
                DisabledDateFunc="@DisabledDay_3_8_20" />
        );

        // Act: select first date, and then the second date
        calendar.Find($"div[value='{DAY_JUNE_5}']")?.Click();
        calendar.Find($"div[value='{DAY_JUNE_7}']")?.Click();

        // Start a new selection, click on a date outside the selection
        calendar.Find($"div[value='{DAY_JUNE_22}']")?.Click();

        // Assert
        Assert.Equal(new[] { DateTime.Parse(DAY_JUNE_22) }, SelectedRange);
    }

    [Fact]
    public void FluentCalendarSelection_SelectModeMultiple()
    {
        IEnumerable<DateTime> SelectedRange = [];

        // Arrange
        var calendar = Render(
            @<FluentCalendar SelectMode="CalendarSelectMode.Multiple"
                @bind-SelectedDates="@SelectedRange"
                PickerMonth="@DateTime.Parse(DAY_JUNE_5)"
                DisabledDateFunc="@DisabledDay_3_8_20" />);

        // Act: select first date, and then the second date
        calendar.Find($"div[value='{DAY_JUNE_5}']")?.Click();
        calendar.Find($"div[value='{DAY_JUNE_7}']")?.Click();

        // Assert
        Assert.Equal(new[] { DateTime.Parse(DAY_JUNE_5), DateTime.Parse(DAY_JUNE_7) }, SelectedRange);
    }

    [Fact]
    public void FluentCalendarSelection_SelectModeMultiple_Unselect()
    {
        IEnumerable<DateTime> SelectedRange = [];

        // Arrange
        var calendar = Render(
            @<FluentCalendar SelectMode="CalendarSelectMode.Multiple"
                @bind-SelectedDates="@SelectedRange"
                PickerMonth="@DateTime.Parse(DAY_JUNE_5)"
                DisabledDateFunc="@DisabledDay_3_8_20" />);

        // Act: select 3 dates
        calendar.Find($"div[value='{DAY_JUNE_5}']")?.Click();
        calendar.Find($"div[value='{DAY_JUNE_6}']")?.Click();
        calendar.Find($"div[value='{DAY_JUNE_7}']")?.Click();

        // Unselect the second date
        calendar.Find($"div[value='{DAY_JUNE_6}']")?.Click();

        // Assert
        Assert.Equal(new[] { DateTime.Parse(DAY_JUNE_5), DateTime.Parse(DAY_JUNE_7) }, SelectedRange);
    }

    [Fact]
    public void FluentCalendarSelection_SelectModeMultiple_SelectDatesHover()
    {
        IEnumerable<DateTime> SelectedRange = [];

        // Arrange
        var calendar = Render(
            @<FluentCalendar SelectMode="CalendarSelectMode.Multiple"
                @bind-SelectedDates="@SelectedRange"
                SelectDatesHover="@Select3Days"
                PickerMonth="@DateTime.Parse(DAY_JUNE_5)"
                DisabledDateFunc="@DisabledDay_3_8_20" />);

        // Act: One click to select 3 dates, using "SelectDatesHover"
        calendar.Find($"div[value='{DAY_JUNE_5}']")?.Click();

        // Assert
        Assert.Equal(new[] { DateTime.Parse(DAY_JUNE_5), DateTime.Parse(DAY_JUNE_6), DateTime.Parse(DAY_JUNE_7) }, SelectedRange);
    }

    // Disable all days with the day number 3, 8, and 20.
    private bool DisabledDay_3_8_20(DateTime date) => date.Day == 3 || date.Day == 8 || date.Day == 20;

    // Always select 3 days, from the "clicked" day.
    private IEnumerable<DateTime> Select3Days(DateTime date) => Enumerable.Range(0, 3).Select(i => date.AddDays(i));
}
